﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Gert3D
{
  public class Raytracer : IRenderer
  {
    private List<Mesh> _meshes = null;
    private List<PointLight> _lights = null;

    public bool Initialize(List<Mesh> meshes, List<PointLight> lights)
    {
      _meshes = meshes;
      _lights = lights;
      return true;
    }

    public bool RenderRegion(Rectangle2 region, Ray[,] rays, Color4[,] result)
    {
      int left = (int)region.Left;
      int right = (int)region.Right;
      int top = (int)region.Top;
      int bottom = (int)region.Bottom;

      Color4 color;

      for (int y = top; y <= bottom; y++)
      {
        for (int x = left; x <= right; x++)
        {
          double minT = double.MaxValue; //Used to exit early from intersection test
          Triangle closestTri = null; //The closest triangle found so far
          IntersectionInfo closestIntersection = null; //The intersection information of the closest intersection found so far
          Ray eyeRay = rays[x, y];
          //Find closest intersection
          foreach (Mesh mesh in _meshes)
          {
            foreach (Triangle tri in mesh.Faces)
            {
              IntersectionInfo info = eyeRay.IntersectTriangle(tri, minT);
              if (info != null)
              {
                minT = info.T;
                closestTri = tri;
                closestIntersection = info;
              }
            }
          }

          //Initialize color
          color.R = 0;
          color.G = 0;
          color.B = 0;
          color.A = 0;

          if (closestIntersection != null)
          {
            color.A = 1.0;
            // Calcluate lighting
            foreach (PointLight light in _lights)
            {
              Ray lightRay = Ray.FromPoints(closestIntersection.IntersectionPoint, light.Position);
              double mintLightT = double.MaxValue;
              bool shadow = false;

              //Check for shadow
              foreach (Mesh mesh in _meshes)
              {
                foreach (Triangle tri in mesh.Faces)
                {
                  if (tri != closestTri)
                  {
                    IntersectionInfo info = lightRay.IntersectTriangle(tri, mintLightT);
                    if (info != null)
                    {
                      mintLightT = info.T;
                      shadow = true;
                      break;
                    }
                  }
                }
              }

              if (!shadow)
              {
                //Calculate diffuse lambert lighting
                double diffuse = Math.Max(0f, Vector3.DotProduct(closestIntersection.Normal, lightRay.Direction)) * light.Intensity;
                color.R += light.Color.R * diffuse;
                color.G += light.Color.G * diffuse;
                color.B += light.Color.B * diffuse;
              }
            }
          }
          result[x, y] = color;

        }
      }

      return true;
    }

  }
}
